
Adélia Cruz
Neural Network Developer

Para automação web, o Browser4 (da PulsarRPA) emergiu como um motor de navegador rápido, seguro para corrotinas, projetado para extração de dados com inteligência artificial. Com capacidades que suportam 100k-200k visitas a páginas complexas por máquina por dia, o Browser4 é construído para escala séria. No entanto, ao extrair dados de sites protegidos, desafios de CAPTCHA tornam-se uma barreira significativa.
CapSolver fornece o complemento perfeito às capacidades de automação do Browser4, permitindo que seus agentes naveguem por páginas protegidas por CAPTCHA de forma semiplena. Essa integração combina a automação de navegador de alto throughput do Browser4 com soluções líderes de mercado para resolução de CAPTCHA.
Browser4 é um framework de automação de navegador de alto desempenho construído em Kotlin. Foi projetado para aplicações de inteligência artificial que exigem capacidades de agentes autônomos, throughput extremo e extração de dados híbrida que combina LLM, algoritmos de aprendizado de máquina e abordagens baseadas em seletores.
| Método | Descrição |
|---|---|
session.open(url) |
Carrega uma página e retorna um PageSnapshot |
session.parse(page) |
Converte o snapshot em documento na memória |
driver.selectFirstTextOrNull(selector) |
Recupera texto do DOM em tempo real |
driver.evaluate(script) |
Executa JavaScript no navegador |
session.extract(document, fieldMap) |
Mapeia seletores CSS para campos estruturados |
CapSolver é um serviço líder de resolução de CAPTCHA que fornece soluções impulsionadas por inteligência artificial para contornar diversos desafios de CAPTCHA. Com suporte para vários tipos de CAPTCHA e tempos de resposta rápidos, o CapSolver se integra de forma eficiente em fluxos de trabalho automatizados.
Ao construir automação com Browser4 que interage com sites protegidos - seja para extração de dados, monitoramento de preços ou pesquisa de mercado - os desafios de CAPTCHA tornam-se uma barreira significativa. Aqui está por que a integração importa:

Maven (pom.xml):
<dependencies>
<!-- Browser4/PulsarRPA -->
<dependency>
<groupId>ai.platon.pulsar</groupId>
<artifactId>pulsar-boot</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Cliente HTTP para CapSolver -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- Análise JSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!-- Corrotinas Kotlin -->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>
Gradle (build.gradle.kts):
dependencies {
implementation("ai.platon.pulsar:pulsar-boot:2.2.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.google.code.gson:gson:2.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
}
Crie um arquivo application.properties:
# Configuração do CapSolver
CAPSOLVER_API_KEY=seu_capsolver_api_key
# Configuração do LLM (opcional, para extração de IA)
OPENROUTER_API_KEY=seu_openrouter_api_key
# Configuração de Proxy (opcional)
PROXY_ROTATION_URL=seu_proxy_url
Aqui está um serviço Kotlin reutilizável que integra o CapSolver ao Browser4:
import com.google.gson.Gson
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import kotlinx.coroutines.delay
import java.util.concurrent.TimeUnit
data class TaskResult(
val gRecaptchaResponse: String? = null,
val token: String? = null,
val cookies: List<Map<String, String>>? = null,
val userAgent: String? = null
)
class CapSolverService(private val apiKey: String) {
private val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
private val gson = Gson()
private val baseUrl = "https://api.capsolver.com"
private val jsonMediaType = "application/json".toMediaType()
private suspend fun createTask(taskData: Map<String, Any>): String {
val payload = mapOf(
"clientKey" to apiKey,
"task" to taskData
)
val request = Request.Builder()
.url("$baseUrl/createTask")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
if (result.get("errorId").asInt != 0) {
throw Exception("Erro do CapSolver: ${result.get("errorDescription").asString}")
}
return result.get("taskId").asString
}
private suspend fun getTaskResult(taskId: String, maxAttempts: Int = 60): TaskResult {
val payload = mapOf(
"clientKey" to apiKey,
"taskId" to taskId
)
repeat(maxAttempts) {
delay(2000)
val request = Request.Builder()
.url("$baseUrl/getTaskResult")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
when (result.get("status")?.asString) {
"ready" -> {
val solution = result.getAsJsonObject("solution")
return TaskResult(
gRecaptchaResponse = solution.get("gRecaptchaResponse")?.asString,
token = solution.get("token")?.asString,
userAgent = solution.get("userAgent")?.asString
)
}
"failed" -> throw Exception("Tarefa falhou: ${result.get("errorDescription")?.asString}")
}
}
throw Exception("Tempo esgotado ao aguardar a solução do CAPTCHA")
}
suspend fun solveReCaptchaV2(websiteUrl: String, websiteKey: String): String {
val taskId = createTask(mapOf(
"type" to "ReCaptchaV2TaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey
))
val result = getTaskResult(taskId)
return result.gRecaptchaResponse ?: throw Exception("Nenhum gRecaptchaResponse na solução")
}
suspend fun solveReCaptchaV3(
websiteUrl: String,
websiteKey: String,
pageAction: String = "submit"
): String {
val taskId = createTask(mapOf(
"type" to "ReCaptchaV3TaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey,
"pageAction" to pageAction
))
val result = getTaskResult(taskId)
return result.gRecaptchaResponse ?: throw Exception("Nenhum gRecaptchaResponse na solução")
}
suspend fun solveTurnstile(
websiteUrl: String,
websiteKey: String,
action: String? = null,
cdata: String? = null
): String {
val taskData = mutableMapOf(
"type" to "AntiTurnstileTaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey
)
// Adicionar metadados opcionais
if (action != null || cdata != null) {
val metadata = mutableMapOf<String, String>()
action?.let { metadata["action"] = it }
cdata?.let { metadata["cdata"] = it }
taskData["metadata"] = metadata
}
val taskId = createTask(taskData)
val result = getTaskResult(taskId)
return result.token ?: throw Exception("Nenhum token na solução")
}
suspend fun checkBalance(): Double {
val payload = mapOf("clientKey" to apiKey)
val request = Request.Builder()
.url("$baseUrl/getBalance")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
return result.get("balance")?.asDouble ?: 0.0
}
}
import ai.platon.pulsar.context.PulsarContexts
import ai.platon.pulsar.skeleton.session.PulsarSession
import kotlinx.coroutines.runBlocking
class ReCaptchaV2Extractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithCaptcha(targetUrl: String, siteKey: String): Map<String, Any?> {
println("Resolvendo reCAPTCHA v2...")
// Resolva o CAPTCHA primeiro
val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)
println("CAPTCHA resolvido, comprimento do token: ${token.length}")
// Crie sessão e abra a página
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Injete o token em um campo de texto oculto usando a propriedade value (seguro)
driver?.evaluate("""
(function() {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = arguments[0];
})('$token');
""")
// Submeta o formulário
driver?.evaluate("document.querySelector('form').submit();")
// Aguarde a navegação
Thread.sleep(3000)
// Extraia dados da página de resultado
val document = session.parse(page)
mapOf(
"título" to document.selectFirstTextOrNull("h1"),
"conteúdo" to document.selectFirstTextOrNull(".conteúdo"),
"sucesso" to (document.body().text().contains("sucesso", ignoreCase = true))
)
}
}
fun main() = runBlocking {
val apiKey = System.getenv("CAPSOLVER_API_KEY") ?: "sua_api_key"
val capSolver = CapSolverService(apiKey)
val extractor = ReCaptchaV2Extractor(capSolver)
val result = extractor.extractWithCaptcha(
targetUrl = "https://exemplo.com/página-protegida",
siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC"
)
println("Resultado da extração: $result")
}
class ReCaptchaV3Extractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithCaptchaV3(
targetUrl: String,
siteKey: String,
action: String = "submit"
): Map<String, Any?> {
println("Resolvendo reCAPTCHA v3 com ação: $action")
// Resolva reCAPTCHA v3 com ação de página personalizada
val token = capSolver.solveReCaptchaV3(
websiteUrl = targetUrl,
websiteKey = siteKey,
pageAction = action
)
println("Token obtido com sucesso")
// Crie sessão e abra a página
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Injete token em entrada oculta (usando atribuição segura de valor)
driver?.evaluate("""
(function(tokenValue) {
var input = document.querySelector('input[name="g-recaptcha-response"]');
if (input) {
input.value = tokenValue;
} else {
var hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = 'g-recaptcha-response';
hidden.value = tokenValue;
var form = document.querySelector('form');
if (form) form.appendChild(hidden);
}
})('$token');
""")
// Clique no botão de envio
driver?.evaluate("document.querySelector('#submit-btn').click();")
Thread.sleep(3000)
val document = session.parse(page)
mapOf(
"resultado" to document.selectFirstTextOrNull(".dados-resultados"),
"status" to "sucesso"
)
}
}
class TurnstileExtractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithTurnstile(targetUrl: String, siteKey: String): Map<String, Any?> {
println("Resolvendo Cloudflare Turnstile...")
// Resolva com metadados opcionais (ação e cdata)
val token = capSolver.solveTurnstile(
targetUrl,
siteKey,
action = "login", // opcional
cdata = "0000-1111-2222-3333-example" // opcional
)
println("Turnstile resolvido!")
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Injetar token Turnstile (usando atribuição segura de valor)
driver?.evaluate("""
(function(tokenValue) {
var input = document.querySelector('input[name="cf-turnstile-response"]');
if (input) input.value = tokenValue;
})('$token');
""")
// Enviar formulário
driver?.evaluate("document.querySelector('form').submit();")
Thread.sleep(3000)
val document = session.parse(page)
mapOf(
"title" to document.selectFirstTextOrNull("title"),
"content" to document.selectFirstTextOrNull("body")?.take(500)
)
}
}
---
## Integração com Browser4 X-SQL
O X-SQL do Browser4 fornece capacidades poderosas de extração. Aqui está como combiná-lo com a resolução de CAPTCHA:
```kotlin
class XSqlCaptchaExtractor(
private val capSolver: CapSolverService
) {
suspend fun extractProductsWithCaptcha(
targetUrl: String,
siteKey: String
): List<Map<String, Any?>> {
// Resolver CAPTCHA antes
val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)
// Criar sessão e estabelecer sessão autenticada
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
document.querySelector('form').submit();
})('$token');
""")
Thread.sleep(3000)
// Agora analisar a página e extrair dados de produtos
val document = session.parse(page)
// Extrair dados de produtos usando métodos embutidos da sessão
val products = mutableListOf<Map<String, Any?>>()
val productElements = document.select(".product-item")
for ((index, element) in productElements.withIndex()) {
if (index >= 50) break // LIMITE DE 50
products.add(mapOf(
"name" to element.selectFirstTextOrNull(".product-name"),
"price" to element.selectFirstTextOrNull(".price")?.let {
"""(\d+\.?\d*)""".toRegex().find(it)?.groupValues?.get(1)?.toDoubleOrNull() ?: 0.0
},
"rating" to element.selectFirstTextOrNull(".rating")
))
}
return products.map { row ->
mapOf(
"name" to row["name"],
"price" to row["price"],
"rating" to row["rating"],
"image_url" to row["image_url"]
)
}
}
}
Para sites que exigem CAPTCHA antes de acessar conteúdo, use um fluxo de trabalho de pré-autenticação:
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
class PreAuthenticator(
private val capSolver: CapSolverService
) {
data class AuthSession(
val cookies: Map<String, String>,
val userAgent: String?
)
suspend fun authenticateWithCaptcha(
loginUrl: String,
siteKey: String
): AuthSession {
// Resolver CAPTCHA
val captchaToken = capSolver.solveReCaptchaV2(loginUrl, siteKey)
// Enviar CAPTCHA para obter cookies de sessão
val client = OkHttpClient.Builder()
.cookieJar(object : CookieJar {
private val cookies = mutableListOf<Cookie>()
override fun saveFromResponse(url: HttpUrl, cookieList: List<Cookie>) {
cookies.addAll(cookieList)
}
override fun loadForRequest(url: HttpUrl): List<Cookie> = cookies
})
.build()
val formBody = okhttp3.FormBody.Builder()
.add("g-recaptcha-response", captchaToken)
.build()
val request = Request.Builder()
.url(loginUrl)
.post(formBody)
.build()
val response = client.newCall(request).execute()
// Extrair cookies da resposta
val responseCookies = response.headers("Set-Cookie")
.associate { cookie ->
val parts = cookie.split(";")[0].split("=", limit = 2)
parts[0] to (parts.getOrNull(1) ?: "")
}
return AuthSession(
cookies = responseCookies,
userAgent = response.request.header("User-Agent")
)
}
}
class AuthenticatedExtractor(
private val preAuth: PreAuthenticator,
private val capSolver: CapSolverService
) {
suspend fun extractWithAuth(
loginUrl: String,
targetUrl: String,
siteKey: String
): Map<String, Any?> {
// Pré-autenticar
val authSession = preAuth.authenticateWithCaptcha(loginUrl, siteKey)
println("Sessão estabelecida com ${authSession.cookies.size} cookies")
// Criar sessão do Browser4
val session = PulsarContexts.createSession()
// Configurar sessão com cookies
val cookieScript = authSession.cookies.entries.joinToString(";") { (k, v) ->
"$k=$v"
}
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Definir cookies
driver?.evaluate("document.cookie = '$cookieScript';")
// Recarregar com sessão autenticada
driver?.evaluate("location.reload();")
Thread.sleep(2000)
// Extrair dados
val document = session.parse(page)
return mapOf(
"authenticated" to true,
"content" to document.selectFirstTextOrNull(".protected-content"),
"userData" to document.selectFirstTextOrNull(".user-profile")
)
}
}
As capacidades de IA do Browser4 podem ser aprimoradas com o OpenRouter, um gateway de API unificado para acessar vários modelos de linguagem. Isso permite extração inteligente de conteúdo que se adapta a diferentes estruturas de página.
import com.google.gson.Gson
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit
data class ChatMessage(val role: String, val content: String)
data class ChatCompletion(val content: String, val model: String, val usage: TokenUsage)
data class TokenUsage(val promptTokens: Int, val completionTokens: Int, val totalTokens: Int)
class OpenRouterService(private val apiKey: String) {
private val client = OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build()
private val gson = Gson()
private val baseUrl = "https://openrouter.ai/api/v1"
private val jsonMediaType = "application/json".toMediaType()
fun chat(
messages: List<ChatMessage>,
model: String = "openai/gpt-4o-mini"
): ChatCompletion {
val payload = mapOf(
"model" to model,
"messages" to messages.map { mapOf("role" to it.role, "content" to it.content) }
)
val request = Request.Builder()
.url("$baseUrl/chat/completions")
.header("Authorization", "Bearer $apiKey")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
val choice = result.getAsJsonArray("choices")?.get(0)?.asJsonObject
val content = choice?.getAsJsonObject("message")?.get("content")?.asString ?: ""
val usage = result.getAsJsonObject("usage")
return ChatCompletion(
content = content,
model = result.get("model")?.asString ?: model,
usage = TokenUsage(
promptTokens = usage?.get("prompt_tokens")?.asInt ?: 0,
completionTokens = usage?.get("completion_tokens")?.asInt ?: 0,
totalTokens = usage?.get("total_tokens")?.asInt ?: 0
)
)
}
fun extractStructuredData(html: String, schema: String): String {
val prompt = """
Extraia os seguintes dados deste conteúdo HTML.
Retorne APENAS JSON válido correspondendo a este esquema: $schema
HTML:
${html.take(4000)}
""".trimIndent()
return chat(listOf(ChatMessage("user", prompt))).content
}
fun listModels(): List<String> {
val request = Request.Builder()
.url("$baseUrl/models")
.header("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
return result.getAsJsonArray("data")?.mapNotNull {
it.asJsonObject.get("id")?.asString
} ?: emptyList()
}
}
Combine resolução de CAPTCHA com extração inteligente de conteúdo:
class SmartExtractor(
private val capSolver: CapSolverService,
private val openRouter: OpenRouterService
) {
suspend fun extractWithAI(
targetUrl: String,
siteKey: String?,
extractionPrompt: String
): Map<String, Any?> {
// Passo 1: Resolver CAPTCHA se necessário
val captchaToken = siteKey?.let {
println("Resolvendo CAPTCHA...")
capSolver.solveReCaptchaV2(targetUrl, it)
}
// Passo 2: Criar sessão e abrir página
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
captchaToken?.let { token ->
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
var form = document.querySelector('form');
if (form) form.submit();
})('$token');
""")
Thread.sleep(3000)
}
// Passo 3: Extrair conteúdo da página
val document = session.parse(page)
val pageContent = document.body().text().take(8000)
// Passo 4: Usar IA para extrair dados estruturados
val llmResponse = openRouter.chat(listOf(
ChatMessage("system", "Você é um assistente de extração de dados. Extraia dados estruturados de páginas da web."),
ChatMessage("user", """
$extractionPrompt
Conteúdo da página:
$pageContent
""".trimIndent())
))
println("LLM usou ${llmResponse.usage.totalTokens} tokens")
return mapOf(
"url" to targetUrl,
"captchaSolved" to (captchaToken != null),
"extractedData" to llmResponse.content,
"tokensUsed" to llmResponse.usage.totalTokens
)
}
}
// Uso
fun main() = runBlocking {
val capSolver = CapSolverService(System.getenv("CAPSOLVER_API_KEY")!!)
val openRouter = OpenRouterService(System.getenv("OPENROUTER_API_KEY")!!)
val extractor = SmartExtractor(capSolver, openRouter)
val result = extractor.extractWithAI(
targetUrl = "https://example.com/products",
siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC",
extractionPrompt = """
Extraia todos os produtos com:
- nome
- preço (como número)
- disponibilidade (em_estoque/fora_de_estoque)
- avaliação (1-5)
Retorne como array JSON.
""".trimIndent()
)
println("Resultado da extração: ${result["extractedData"]}")
}
Use LLM para gerar seletores CSS para estruturas de página desconhecidas:
class AdaptiveExtractor(
private val capSolver: CapSolverService,
private val openRouter: OpenRouterService
) {
suspend fun extractWithAdaptiveSelectors(
targetUrl: String,
siteKey: String?,
dataFields: List<String>
): Map<String, Any?> {
// Resolver CAPTCHA primeiro
val token = siteKey?.let { capSolver.solveReCaptchaV2(targetUrl, it) }
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
token?.let { t ->
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
})('$t');
""")
}
// Obter estrutura HTML da página
val htmlSample = driver?.evaluate("document.body.innerHTML")?.toString()?.take(5000) ?: ""
// Pedir ao LLM para gerar seletores
val selectorPrompt = """
Analise este HTML e forneça seletores CSS para estes campos: ${dataFields.joinToString(", ")}
Amostra de HTML:
$htmlSample
Retorne JSON como: {"fieldName": "css-selector", ...}
""".trimIndent()
val selectorsJson = openRouter.chat(listOf(ChatMessage("user", selectorPrompt))).content
val selectors = Gson().fromJson(selectorsJson, Map::class.java) as Map<String, String>
// Extrair usando seletores gerados
val document = session.parse(page)
val extractedData = selectors.mapValues { (_, selector) ->
document.selectFirstTextOrNull(selector)
}
return mapOf(
"url" to targetUrl,
"selectors" to selectors,
"data" to extractedData
)
}
}
O design seguro para coroutines do Browser4 permite gerenciamento eficiente de CAPTCHA em paralelo:
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
data class ExtractionJob(
val url: String,
val siteKey: String?
)
data class ExtractionResult(
val url: String,
val data: Map<String, Any?>?,
val captchaSolved: Boolean,
val error: String?,
val duration: Long
)
class ParallelExtractor(
private val capSolver: CapSolverService,
private val concurrency: Int = 5
) {
suspend fun extractAll(jobs: List<ExtractionJob>): List<ExtractionResult> = coroutineScope {
val channel = Channel<ExtractionJob>(Channel.UNLIMITED)
val results = mutableListOf<ExtractionResult>()
// Enviar todos os trabalhos para o canal
jobs.forEach { channel.send(it) }
channel.close()
// Processar com concorrência limitada
val workers = (1..concorrência).map { workerId ->
async {
val workerResults = mutableListOf<ExtractionResult>()
// Cada worker cria sua própria sessão para segurança de thread
val workerSession = PulsarContexts.createSession()
for (job in channel) {
val startTime = System.currentTimeMillis()
var captchaResolvido = false
try {
// Resolver CAPTCHA se fornecido a chave do site
val token = job.chaveDoSite?.let {
captchaResolvido = true
capSolver.resolverReCaptchaV2(job.url, it)
}
// Extrair dados
val page = workerSession.open(job.url)
token?.let { t ->
val driver = workerSession.getOrCreateBoundDriver()
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
})('$t');
""")
}
val document = workerSession.parse(page)
workerResults.add(ExtractionResult(
url = job.url,
dados = mapOf(
"título" to document.selectFirstTextOrNull("title"),
"h1" to document.selectFirstTextOrNull("h1")
),
captchaResolvido = captchaResolvido,
erro = null,
duração = System.currentTimeMillis() - startTime
))
} catch (e: Exception) {
workerResults.add(ExtractionResult(
url = job.url,
dados = null,
captchaResolvido = captchaResolvido,
erro = e.message,
duração = System.currentTimeMillis() - startTime
))
}
}
workerResults
}
}
workers.awaitAll().flatten()
}
}
// Uso
fun main() = runBlocking {
val capSolver = CapSolverService(System.getenv("CAPSOLVER_API_KEY")!!)
val extractor = ParallelExtractor(capSolver, concorrência = 5)
val jobs = listOf(
ExtractionJob("https://site1.com/dados", "6Lc..."),
ExtractionJob("https://site2.com/dados", null),
ExtractionJob("https://site3.com/dados", "6Lc..."),
)
val results = extractor.extractAll(jobs)
val resolvidos = results.count { it.captchaResolvido }
println("Concluído ${results.size} extrações, resolvidos $resolvidos CAPTCHAs")
results.forEach { r ->
println("${r.url}: ${r.duração}ms - ${r.erro ?: "sucesso"}")
}
}
suspend fun <T> withRetry(
maxTentativas: Int = 3,
tempoInicial: Long = 1000,
block: suspend () -> T
): T {
var últimaExceção: Exception? = null
repeat(maxTentativas) { tentativa ->
try {
return block()
} catch (e: Exception) {
últimaExceção = e
println("Tentativa ${tentativa + 1} falhou: ${e.message}")
delay(tempoInicial * (tentativa + 1))
}
}
throw últimaExceção ?: Exception("Máximo de tentativas excedido")
}
// Uso
val token = withRetry(maxTentativas = 3) {
capSolver.resolverReCaptchaV2(url, chaveDoSite)
}
suspend fun garantirSaldoSuficiente(
capSolver: CapSolverService,
saldoMínimo: Double = 1.0
) {
val saldo = capSolver.verificarSaldo()
if (saldo < saldoMínimo) {
throw Exception("Saldo insuficiente do CapSolver: $${"%.2f".format(saldo)}. Por favor, recarregue.")
}
println("Saldo do CapSolver: $${"%.2f".format(saldo)}")
}
class CacheDeToken(private val ttlMs: Long = 90_000) {
private data class TokenCacheado(val token: String, val timestamp: Long)
private val cache = mutableMapOf<String, TokenCacheado>()
private fun getKey(domínio: String, chaveDoSite: String) = "$domínio:$chaveDoSite"
fun get(domínio: String, chaveDoSite: String): String? {
val key = getKey(domínio, chaveDoSite)
val cached = cache[key] ?: return null
if (System.currentTimeMillis() - cached.timestamp > ttlMs) {
cache.remove(key)
return null
}
return cached.token
}
fun set(domínio: String, chaveDoSite: String, token: String) {
val key = getKey(domínio, chaveDoSite)
cache[key] = TokenCacheado(token, System.currentTimeMillis())
}
}
// Uso com cache
class CapSolverComCache(
private val capSolver: CapSolverService,
private val cache: CacheDeToken = CacheDeToken()
) {
suspend fun resolverReCaptchaV2ComCache(urlDoSite: String, chaveDoSite: String): String {
val domínio = java.net.URL(urlDoSite).host
cache.get(domínio, chaveDoSite)?.let {
println("Usando token em cache")
return it
}
val token = capSolver.resolverReCaptchaV2(urlDoSite, chaveDoSite)
cache.set(domínio, chaveDoSite, token)
return token
}
}
| Configuração | Descrição | Padrão |
|---|---|---|
CAPSOLVER_API_KEY |
Sua chave de API do CapSolver | - |
OPENROUTER_API_KEY |
Sua chave de API do OpenRouter para recursos de LLM | - |
PROXY_ROTATION_URL |
URL do serviço de rotação de proxy | - |
O Browser4 usa application.properties para configuração adicional |
Integrar o CapSolver com o Browser4 cria uma combinação poderosa para extração de dados da web de alto throughput. A arquitetura do Browser4 segura para coroutines e suas capacidades de desempenho extremo, combinadas com a resolução confiável de CAPTCHAs do CapSolver, permitem extração em escala.
Padrões de integração principais:
Seja você estiver construindo sistemas de monitoramento de preços, pipelines de pesquisa de mercado ou plataformas de agregação de dados, a combinação Browser4 + CapSolver fornece a confiabilidade e a escalabilidade necessárias para ambientes de produção.
Pronto para começar? Registre-se no CapSolver e use o código de bônus BROWSER4 para obter um bônus extra de 6% na primeira recarga!
O Browser4 é um framework de automação de navegador seguro para coroutines da PulsarRPA. Ele é construído em Kotlin e projetado para extração de dados com inteligência artificial, suportando 100k-200k visitas a páginas complexas por máquina por dia.
O CapSolver se integra ao Browser4 por meio de uma classe de serviço que resolve CAPTCHAs via API do CapSolver. Os tokens resolvidos são então injetados nas páginas usando as capacidades de avaliação de JavaScript do Browser4 (driver.evaluate()).
O CapSolver suporta reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge (5s), AWS WAF, GeeTest v3/v4 e muitos outros.
O CapSolver oferece preços competitivos com base no tipo e volume de CAPTCHAs resolvidos. Visite capsolver.com para os preços atuais. Use o código BROWSER4 para obter um bônus de 6%.
O Browser4 é construído em Kotlin e roda na JVM (Java 17+). Também pode ser usado em aplicações Java.
Sim! O design seguro para coroutines do Browser4 permite processamento eficiente em paralelo. Combinado com a API do CapSolver, você pode resolver múltiplos CAPTCHAs simultaneamente em diferentes trabalhos de extração.
A chave do CAPTCHA geralmente está presente no código fonte da página:
data-sitekey no elemento .g-recaptchadata-sitekey no elemento .cf-turnstileAprenda arquitetura de raspagem web escalável em Rust com reqwest, scraper, raspagem assíncrona, raspagem de navegador headless, rotação de proxies e tratamento de CAPTCHA compatível.

Compare o Selenium vs Puppeteer para resolver CAPTCHA. Descubra benchmarks de desempenho, notas de estabilidade e como integrar o CapSolver para o máximo de sucesso.
